#include "../headers/Mailer.h"
#include <string>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include "../headers/RoundRobin.h"
#include "../headers/MultiLevel.h"
#include "../headers/Awesome.h"
#include "../headers/ApproxSjf.h"
//#include <time.h>

extern int N;

using namespace std;


/* Declaration of the wrapper function */
extern "C"
{
	void *run_mailer(void *args)
	{
		cout << "initiating Mailer thread" <<endl;
		Mailer *curThread = (Mailer *)args;
		curThread->run();
		return NULL;
	}
}


Mailer::Mailer(int buffer) : Mailbox(buffer), workers(N)
{

}

void Mailer::addNode(int node, vector<int>* neighbors) {
	workers[node] = new Worker(node, this, neighbors);
	cout << "adding node " << node <<  ", neighbors: ";
	for (vector<int>::iterator iter = (*neighbors).begin(); iter != (*neighbors).end(); ++iter ) {
		cout << *iter<<" ";
	}
	cout << ". node added"<<endl;
}

bool Mailer::killNode(int id) {
	if(id>=N | id<=0)
		return false;

	for(int i=1; i<N; i++) {
		workers[i]->initialize();
	}

	workers[id]->kill();

	for(int i=1; i<N; i++) {
		if(workers[i]->alive) {
			workers[i]->revive();
			break;
		}
	}
	waitForSteadyState();

	return true;
}

bool Mailer::reviveNode(int id) {
	if(id>=N | id<=0)
		return false;
	workers[id]->revive();
	waitForSteadyState();
	return true;
}

bool Mailer::killAll() {
	for(int i=1; i<N; i++) {
		workers[i]->kill();
	}
	return true;
}

void Mailer::printRT(int id) {
	if(id>=N | id<=0)
		return;
	cout << endl << "Routing table for node "<<id<<endl<<workers[id]->table;
}

bool Mailer::sendPacket(int sourceId, int targetId, string msg) {
	if(sourceId>=N | sourceId<=0 | targetId>=N | targetId<=0)
		return false;
	submitMessage(new SystemMessage(sourceId, targetId, msg));
	return true;
}


bool Mailer::start(int scheduleNum, int arg) {
	cout<<"Mailer::start"<<endl;

	switch(scheduleNum) {
		case 1:
			scheduler = new RoundRobin((vector<OSThread*>*)&workers, arg);
			break;
		case 2:
			scheduler = new MultiLevel((vector<OSThread*>*)&workers, arg);
			break;
		case 3:
			scheduler = new Awesome((vector<OSThread*>*)&workers, arg);
			break;
		case 4:
			scheduler = new ApproxSjf((vector<OSThread*>*)&workers, arg);
			break;

	}


	//start all worker threads:
	for(int i=1; i<N; i++) {
		workers[i]->start();
	}

	workers[1]->revive();

	/* Holds the ID of the created thread */
	pthread_t	m_thread;

	//start the mailer thread:
	if (pthread_create(&m_thread, NULL, run_mailer, this) != 0) {
		perror("Mailer thread creation failed");
		exit(1);
	}
	//usleep(1000);

	waitForSteadyState();

	return true;
}


void Mailer::waitForSteadyState(){
	cout << "mailer/scheduler is waiting for steady state..." <<endl;

	do {
		//cout << "not yet..."<<endl;
		usleep(100);
	} while(!inSteadyState());

	cout << "******************* STEADY STATE :) ******************"<<endl;
	printCounters();
	scheduler->contextSwitches=0;
	scheduler->messageQuantoms=0;
}


bool Mailer::inSteadyState(){
	if (getMsgCount()>0)
		return false;
	for(int i=1; i<N; i++)
		if(workers[i]->busy)
			return false;
	return true;
}

Mailer::~Mailer() {
	for (vector<Worker*>::const_iterator iter = workers.begin(); iter != workers.end(); ++iter )
		delete *iter;
}

ostream& operator<< ( std::ostream& output, const Mailer& t) {
	output << "Mailer lol"<<endl;
	return output;
}

void Mailer::printCounters() {
	cout << "since the last stabilization, there has been " <<
		scheduler->contextSwitches << " Context switches, and "<<
		scheduler->messageQuantoms << " Message Quantoms were handed out."<<endl;
}



void Mailer::run(){
	cout << "Mailer::run"<<endl;
	//sleep(1);
	//cout << "Mailer main loop"<<endl;
	//.. setup?
	while(true) {
		if(!inSteadyState()) {
			//cout << "Mailer main loop"<<endl;
			//cout << "Messages in system: "<<getMsgCount()<<endl;
			scheduler->runSomeThread();
			//cout << "Messages in system: "<<getMsgCount()<<endl;
			//at this point, ALL THREADS ARE BLOCKED
			//todo: assert this somehow

			//cout << "Mailer checking messages loop"<<endl;
			//pass messages
			while(hasNewMessage()) {
				//cout << "Mailer's got mail!"<<endl;
				Message* msg = popMessage();
				//cout << "Mailer forwards "<<*msg << endl;
				workers[msg->target]->submitMessage(msg);
				scheduler->increasePriority(msg->target);
			}

			//cout << "Mailer's taking a nap"<<endl;
		}
		usleep(1000);
	}

}
